import [Value,Scope,Symbol,Errors]
import structs/ArrayList

//TODO: currently only (elem1 elem2 elem3 elem4) [i.e. proper ] lists are supported, add support for dot-lists (improper lists).
vList : class extends Value {
    values : ArrayList<Value>
    
    init : func~full(=values)
    init : func~fullwl(=values,=line)
    init : func~empty {
        init(ArrayList<Value> new())
    }
    init : func~emptywl(=line) { init~empty() }
    
    equals? : func(other : Value) -> Bool {
        if(other class != This) { return false }
        if(other as This values size != this values size) { return false }
    
        for(i in 0 .. this values size) {
            if(!this values[i] equals?(other as This values[i])) {
                return false
            }
        }
        true
    }
    
    clone : func -> Value {
        vals := ArrayList<Value> new()
        for(val in values) {
            vals add(val clone())
        }
        return This new(vals,line)
    }
    
    replaceAll : func (what,with : Value) {
        for(i in 0 .. values size) {
            if(values[i] equals?(what)) {
                values[i] = with
            } else if(values[i] class == This) {
                values[i] as This replaceAll(what,with)
            }
        }
    }
    
    commaLoop : func (scope : Scope) -> ArrayList<Value> {
        ret := ArrayList<Value> new()
        for(val in values) {
            if(val class != vList) {
                ret add(val)
            } else {
                if(!val as vList nil?()) {
                    if(!val as vList values[0] equals?(Symbol new("comma")) && !val as vList values[0] equals?(Symbol new("comma-at"))) {
                        ret add(vList new(val as vList commaLoop(scope)))
                    } else {
                        if(val as vList values[0] equals?(Symbol new("comma"))) {
                            if(val as vList values size == 2) { // Comma implementation :D 
                                nArg := val as vList values[1] evaluate(scope)
                                ret add(nArg)
                            } else {
                                ArgumentsNotEqualToError new(",",1,val as vList values size-1,val line) throw()
                            }
                        } else {
                            if(val as vList values size == 2) { // Comma-at implementation =)
                                nArg := val as vList values[1] evaluate(scope)
                                if(nArg class == vList) {
                                    for(val in nArg as vList values) {
                                        ret add(val)
                                    }
                                } else {
                                    ret add(nArg)
                                }
                            } else {
                                ArgumentsNotEqualToError new(",@",1,val as vList values size-1,val line) throw()
                            }
                        }
                    }
                } else {
                    ret add(vList new())
                }
            }
        }
        ret
    }
    
    contains? : func(some : Value) -> Bool {
        for(val in values) {
            if(val equals?(some)) {
                return true
            }
        }
        false
    }
    
    toString : func -> String {
        if(values size == 0) { return "nil" }
        stritems := values map(|x| x toString())
        "(%s)" format(stritems join(" "))
    }
    
    evaluate : func(scope : Scope) -> Value {
        if(values size == 0) {
            return this
        }
    
        caller := values[0] evaluate(scope)
        args : ArrayList<Value>
        if(values size == 1) {
            args = ArrayList<Value> new()
        }
        else {
            args = values[1..-1]
        }
    
        caller call(args,scope)
    }
    
    nil? : func -> Bool { values size == 0 }
}